// SPDX-License-Identifier: GPL-2.0
/* smc_sysfs.c - sysfs interface  of LomboTech SMC module
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/sysfs.h>
#include "smc.h"

// tran_mode sysfs node
static ssize_t
tran_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	ssize_t ret;

	ret = smc_read_transfer_mode();
	return sprintf(buf, "transfer protocol : T%ld\n", ret);
}
static DEVICE_ATTR_RO(tran_mode);

// special_mode sysfs node
static ssize_t
special_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	ssize_t ret;

	ret = smc_read_specail_operation();
	return sprintf(buf, "special mode : %ld\n", ret);
}
static DEVICE_ATTR_RO(special_mode);

// edc_type sysfs node
static ssize_t
edc_type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	ssize_t ret;
	const char *temp[2] = {"LRC", "CRC"};

	ret = smc_read_block_edc_type();
	return sprintf(buf, "edc type mode : %s\n", temp[ret]);
}
static DEVICE_ATTR_RO(edc_type);

static struct attribute *smc_attrs[] = {
	&dev_attr_tran_mode.attr,
	&dev_attr_special_mode.attr,
	&dev_attr_edc_type.attr,
	NULL,
};

static struct attribute_group smc_attr_group = {
	.attrs		= smc_attrs,
};

static const struct attribute_group *smc_attr_groups[] = {
	&smc_attr_group,
	NULL,
};

const struct attribute_group **smc_get_dev_attribute_groups(void)
{
	return smc_attr_groups;
}

int smc_add_groups(struct smc_device *smc, const struct attribute_group **grps)
{
	size_t old_cnt = 0, add_cnt = 0, new_cnt;
	const struct attribute_group **groups, **old;

	if (smc->registered)
		return -EINVAL;
	if (!grps)
		return -EINVAL;

	groups = smc->dev.groups;
	if (groups)
		for (; *groups; groups++)
			old_cnt++;

	for (groups = grps; *groups; groups++)
		add_cnt++;

	new_cnt = old_cnt + add_cnt + 1;
	groups = devm_kcalloc(&smc->dev, new_cnt, sizeof(*groups), GFP_KERNEL);
	if (IS_ERR_OR_NULL(groups))
		return PTR_ERR(groups);
	memcpy(groups, smc->dev.groups, old_cnt * sizeof(*groups));
	memcpy(groups + old_cnt, grps, add_cnt * sizeof(*groups));
	groups[old_cnt + add_cnt] = NULL;

	old = smc->dev.groups;
	smc->dev.groups = groups;
	if (old && old != smc_attr_groups)
		devm_kfree(&smc->dev, old);

	return 0;
}
EXPORT_SYMBOL(smc_add_groups);

int smc_add_group(struct smc_device *smc, const struct attribute_group *grp)
{
	const struct attribute_group *groups[] = {grp, NULL};

	return smc_add_groups(smc, groups);
}
EXPORT_SYMBOL(smc_add_group);
